/** ***************************************************************************
 * SugarRaw.cpp
 * 2012 CoffeeParty Ltd.
 * Date Created: 2012-07-21                                  format YYYY-MM-DD
 * Revision Id: $Id$
 ******************************************************************************
 */

#include "CoffeeCommons.hpp"
#include "CoffeeLogger.hpp"
#include "CoffeeIO.hpp"

#include "SugarImage.hpp"

SugarRaw::SugarRaw(c_const_bytep filename)
{
    if (!isCompatibleFileFormat(filename, "bmp"))
    {
        c_print("SugarRaw(): File Format Error");
        return;
    }

    if (!open(filename))
    {
        c_print("SugarRaw(): Reading Error");
        return;
    }
}

SugarRaw::SugarRaw(c_int width, c_int height, c_int bpp)
{
    c_uint rmask, gmask, bmask, amask;
    if (bpp == 32)
    {
#if defined(_BIG_ENDIAN__) || SDL_BYTEORDER == SDL_BIG_ENDIAN
        rmask = 0xFF000000;
        gmask = 0x00FF0000;
        bmask = 0x0000FF00;
        amask = 0x000000FF;
#else
        rmask = 0x000000FF;
        gmask = 0x0000FF00;
        bmask = 0x00FF0000;
        amask = 0xFF000000;
#endif
    }
    else
    {
        rmask = 0x000000FF;
        gmask = 0x0000FF00;
        bmask = 0x00FF0000;
        amask = 0x00000000;
    }

    image = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, bpp, rmask,
        gmask, bmask, amask);

    if (!image)
    {
        c_print("Unable to create Image");
        return;
    }
}

SugarRaw::SugarRaw(c_dimension<c_int>& _imagesize, c_int bpp)
{
    c_uint rmask, gmask, bmask, amask;
    if (bpp == 32)
    {
#if defined(_BIG_ENDIAN__) || SDL_BYTEORDER == SDL_BIG_ENDIAN
        rmask = 0xFF000000;
        gmask = 0x00FF0000;
        bmask = 0x0000FF00;
        amask = 0x000000FF;
#else
        rmask = 0x000000FF;
        gmask = 0x0000FF00;
        bmask = 0x00FF0000;
        amask = 0xFF000000;
#endif
    }
    else
    {
        rmask = 0x000000FF;
        gmask = 0x0000FF00;
        bmask = 0x00FF0000;
        amask = 0x00000000;
    }

    image = SDL_CreateRGBSurface(SDL_SWSURFACE, _imagesize.x, _imagesize.y, bpp,
        rmask, gmask, bmask, amask);

    if (!image)
    {
        c_print("Unable to create Image");
        return;
    }
}

SugarRaw::SugarRaw(SugarImage *_image)
{
    image = SDL_CreateRGBSurfaceFrom(_image->ark(), _image->getDimension().x,
        _image->getDimension().y, _image->getBitsPerPixel(), (c_int)_image->getPitch(),
        (c_int)_image->getRedMask(), (c_int)_image->getGreenMask(), (c_int)_image->getBlueMask(),
        (c_int)_image->getAlphaMask());

    if (!image)
    {
        c_print("Unable to create Image");
        return;
    }
}

SugarRaw::SugarRaw(c_dimension<c_int>& _imagesize, c_voidp _imagedata, c_int _imagepitch, c_int _imagebpp)
{
    c_uint rmask, gmask, bmask, amask;
    if (_imagebpp == 32)
    {
#if defined(_BIG_ENDIAN__) || SDL_BYTEORDER == SDL_BIG_ENDIAN
        rmask = 0xFF000000;
        gmask = 0x00FF0000;
        bmask = 0x0000FF00;
        amask = 0x000000FF;
#else
        rmask = 0x000000FF;
        gmask = 0x0000FF00;
        bmask = 0x00FF0000;
        amask = 0xFF000000;
#endif
    }
    else
    {
        rmask = 0x000000FF;
        gmask = 0x0000FF00;
        bmask = 0x00FF0000;
        amask = 0x00000000;
    }

    image = SDL_CreateRGBSurfaceFrom(_imagedata, _imagesize.x, _imagesize.y,
        _imagebpp, (c_int)_imagepitch, (c_int)rmask, (c_int)gmask, (c_int)bmask,
        (c_int)amask);

    if (!image)
    {
        c_print("Unable to create Image");
        return;
    }
}


SugarRaw::~SugarRaw()
{
    if (image)
        SDL_FreeSurface(image);
}

c_size_t SugarRaw::getSize() const
{
    return image->w * image->h * image->format->BitsPerPixel;
}

c_size_t SugarRaw::getImageDataSizeInBytes() const
{
    return image->w * image->h * image->format->BitsPerPixel;
}

c_size_t SugarRaw::getImageDataSizeInPixel() const
{
    return image->w * image->h;
}

c_uint SugarRaw::getBitsPerPixel() const
{
    return image->format->BitsPerPixel;
}

c_uint SugarRaw::getBytesPerPixel() const
{
    return image->format->BytesPerPixel;
}

c_uint SugarRaw::getPitch() const
{
    return image->pitch;
}

c_uint SugarRaw::getRedMask() const
{
    return image->format->Rmask;
}

c_uint SugarRaw::getGreenMask() const
{
    return image->format->Gmask;
}

c_uint SugarRaw::getBlueMask() const
{
    return image->format->Bmask;
}

c_uint SugarRaw::getAlphaMask() const
{
    return image->format->Amask;
}

c_color SugarRaw::getPixel(c_uint x, c_uint y) const
{
    if (x >= (c_uint)image->w || y >= (c_uint)image->h)
        return 0;

    c_uint *pixels = (c_uint *)image->pixels;
    return c_color(pixels[(y * image->w) + x]);
}

c_void SugarRaw::setPixel(c_uint x, c_uint y,
    const c_color &color, c_bool blend)
{
    if (x >= (c_uint)image->w || y >= (c_uint)image->h)
        return;
    c_uint *pixels = (c_uint *)image->pixels;
    pixels[(y * image->w) + x] = color.color;
}

c_voidp SugarRaw::ark()
{
    return image->pixels;
}

c_void SugarRaw::unark() { }

c_voidp SugarRaw::getData() const
{
    return image->pixels;
}

c_uint SugarRaw::getWidth() const
{
    return image->w;
}

c_uint SugarRaw::getHeight() const
{
    return image->h;
}

c_bool SugarRaw::hasAlpha() const
{
    return (image->format->BitsPerPixel == 32);
}

c_void SugarRaw::showOGL()
{
    glDisable(GL_DEPTH_TEST); //!< Disable Z coordinates and depth buffer
    glPixelStorei(GL_UNPACK_ROW_LENGTH, image->w);
    if (getBitsPerPixel() == 32)
    {
        glPixelStorei(GL_UNPACK_ALIGNMENT, 2);
        glDrawPixels(image->w, image->h, GL_RGBA, GL_UNSIGNED_BYTE, image->pixels);
    }
    else if (getBitsPerPixel() == 24)
    {
        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
        glDrawPixels(image->w, image->h, GL_RGB, GL_UNSIGNED_BYTE, image->pixels);
    }
    glEnable(GL_DEPTH_TEST);
}

c_bool SugarRaw::open(c_const_bytep filename, c_bool hasAlpha)
{
    if (!filename)
        return false;

    SDL_Surface *tmp;
    tmp = SDL_LoadBMP(filename);

    if (tmp)
    {
        c_print("Unable to Load File");
        return false;
    }

    if (hasAlpha)
        image = SDL_DisplayFormatAlpha(tmp);
    else image = SDL_DisplayFormat(tmp);

    SDL_FreeSurface(tmp);
    return true;
}

c_bool SugarRaw::isCompatibleFileFormat(c_const_bytep filename, c_const_bytep ext)
{
    c_string tmp = filename;
    c_int epos = tmp.findLast('.');
    if (epos < 0)
        return false;

    epos++;
    if (tmp.equals_ss_ic(ext, epos))
        return true;

    return false;
}

c_dimension<c_uint> SugarRaw::getDimension() const
{
    return c_dimension<c_uint>(image->w, image->h);
}

c_uint SugarRaw::getPixel32(SDL_Surface *other, c_int x, c_int y)
{
    c_uint *pixels = (c_uint *)image->pixels;
    return pixels[(y * image->w) + x];
}

c_void SugarRaw::setPixel32(SDL_Surface *other, c_int x, c_int y, c_uint pixel)
{
    c_uint *pixels = (c_uint *)image->pixels;
    pixels[(y * image->w) + x] = pixel;
}

c_uint SugarRaw::getPixelAny(SDL_Surface *other, c_int x, c_int y)
{
    ///< Address to the pixel we want to retrieve
    c_uint *pixels = (c_uint *)image->pixels + y * image->pitch + x *
        image->format->BytesPerPixel;

    switch(image->format->BytesPerPixel)
    {
    case 1: return *pixels;
    case 2: return *(c_short *)pixels;
    case 3:
        if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
            return pixels[0] << 16 | pixels[1] << 8 | pixels[2];
        else return pixels[0] | pixels[1] << 8 | pixels[2] << 16;
    case 4: return *(c_int *)pixels;
    default: return 0;
    }

    return 0;
}

c_void SugarRaw::setPixelAny(SDL_Surface *other, c_int x, c_int y, c_uint pixel)
{
    ///< Address to the pixel we want to retrieve
    c_uint *pixels = (c_uint *)image->pixels + y * image->pitch + x *
        image->format->BytesPerPixel;

    switch(image->format->BytesPerPixel)
    {
    case 1: *pixels = pixel; break;
    case 2: *(c_short *)pixels = pixel; break;
    case 3:
        if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
        {
            pixels[0] = (pixel >> 16) & 0xFF;
            pixels[1] = (pixel >> 8) & 0xFF;
            pixels[2] = (pixel & 0xFF);
        }
        else
        {
            pixels[0] = (pixel & 0xFF);
            pixels[1] = (pixel >> 8) & 0xFF;
            pixels[2] = (pixel >> 16) & 0xFF;
        }
        break;
    case 4: *(c_int *)pixels = pixel; break;
    }
}

c_void SugarRaw::flipTexture(c_int direction)
{
    SDL_Surface *flip;
    flip = SDL_CreateRGBSurface(SDL_SWSURFACE, image->w, image->h,
        image->format->BitsPerPixel, image->format->Rmask, image->format->Gmask,
        image->format->Bmask, image->format->Amask);

    if (SDL_MUSTLOCK(image))
        SDL_LockSurface(image);

    for (c_int x = 0, rx = flip->w - 1; x < flip->w; x++, rx--)
    {
        for (c_int y = 0, ry = flip->h - 1; y < flip->h; y++, ry--)
        {
            c_uint pixel = getPixel32(image, x, y);
            if ((direction & CPFT_HORIZONTAL) && (direction & CPFT_VERTICAL))
                setPixel32(flip, rx, ry, pixel);
            else if (direction & CPFT_HORIZONTAL)
                setPixel32(flip, rx, y, pixel);
            else if (direction & CPFT_VERTICAL)
                setPixel32(flip, x, ry, pixel);
        }
    }

    if (SDL_MUSTLOCK(image))
        SDL_UnlockSurface(image);

    SDL_FreeSurface(image);

    image = SDL_DisplayFormatAlpha(flip);
}

c_void SugarRaw::scaleTexture(c_int width, c_int height)
{
    c_double stretch_x = 0, stretch_y = 0;

    SDL_Surface *scale;
    scale = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, image->format->BitsPerPixel,
        image->format->Rmask, image->format->Gmask, image->format->Bmask,
        image->format->Amask);

    stretch_x = (static_cast<c_double>(width) / static_cast<c_double>(image->w));
    stretch_y = (static_cast<c_double>(height) / static_cast<c_double>(image->h));

    if (SDL_MUSTLOCK(scale))
        SDL_LockSurface(scale);

    for (c_int y = 0; y < image->h; y++)
    {
        for (c_int x = 0; x < image->w; x++)
        {
            c_uint pixel = getPixel32(image, x, y);
            for (c_int fy = 0; fy < stretch_y; fy++)
            {
                for (c_int fx = 0; fx < stretch_x; fx++)
                {
                    setPixel32(scale, static_cast<c_int>(stretch_x * x) + fx,
                        static_cast<c_int>(stretch_y * y) + fy, pixel);
                }
            }
        }
    }

    if (SDL_MUSTLOCK(image))
        SDL_UnlockSurface(image);

    SDL_FreeSurface(image);
    image = SDL_DisplayFormatAlpha(scale);
}

c_void SugarRaw::writeToDisk(c_const_bytep filename)
{
    if (SDL_SaveBMP(image, filename) < 0)
        c_print("Unable to write to file: %s", SDL_GetError());
}

c_void SugarRaw::toPowerOfTwoTexture()
{
    SDL_Surface *pad;
    c_int padX = 0, padY = 0;

    padX = (image->w & (image->w - 1));
    padY = (image->h & (image->h - 1));
    if ((padX > 0) || (padY > 0))
    {
        if (padX < image->w)
            padX = padX << 0x01;

        if (padY < image->h)
            padY = padY << 0x01;

        c_print("The image isn't in the power of 2!");
        pad = SDL_CreateRGBSurface(SDL_SWSURFACE, padX, padY, image->format->BitsPerPixel,
            image->format->Rmask, image->format->Gmask, image->format->Bmask,
            image->format->Amask );

        if (SDL_MUSTLOCK(image))
            SDL_LockSurface(image);

        for (c_int x = 0; x < image->w; x++)
        {
            for (c_int y = 0; y < image->h; y++)
            {
                c_uint pixel = getPixel32(image, x, y);
                setPixel32(pad, x, y, pixel);
            }
        }

        if (SDL_MUSTLOCK(image))
            SDL_UnlockSurface(image);
    }
}

SugarRaw* OpenRaw(c_const_bytep file)
{
    return new SugarRaw(file);
}

c_void DeleteRaw(SugarRaw* ptr)
{
    if (ptr)
        drop(ptr);
}


